/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Sylvain Sicard - initial API and implementation
 ******************************************************************************/
package fr.jade.console.cmd;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.type.InterfaceType;

import fr.jade.console.core.AbstractCommand;
import fr.jade.console.core.Command;
import fr.jade.console.core.Environment;

public class Call extends AbstractCommand implements Command {

  public String getDescription(){
    return "calls a method on a component";
  }
  
  public String getHelp(){
    return "call <itfName> <methodName> <param,..,param>\n"+
           "call <itfName>";
  }
  /**
   * invoke a method.
   * 
   * @param pargs
   *          args[0] = cmdName
   *          args[1] = itfName
   *          args[2] = methodName
   *          args[3] = param,...,param 
   */

  public Object execute(String[] args, Environment env) throws Throwable {
    Component current = getCurrent(env);
    Object result = null;
    switch(args.length){
    case 3:
      result = callMethod(current, args[1], args[2], new String[0]);
      break;
    case 4:
      String[] keys = args[3].split(",");
      Object[] arguments = new Object[keys.length];
      int i = 0;
      for(String s : keys){
        if (env.isSetEnvVar(s)){
          arguments[i++]=env.getEnvVar(s);
        }else{
          arguments[i++]=s;
        }
      }
      result = callMethod(current, args[1], args[2], arguments);
      break;
    }
    env.setEnvVar("tmp", result);
    return null;
  }

  protected Object callMethod(Component current, String itfName, String methodName, Object[] arguments) throws Throwable {
    try {
      Object itf = current.getFcInterface(itfName);
      Class<?>[] parameterTypes = new Class[arguments.length];
      for(int i=0; i<arguments.length; i++){
        Class<?> c = arguments[i].getClass();
        if(Proxy.isProxyClass(c)){
          String signature = ((InterfaceType)((Interface)arguments[i]).getFcItfType()).getFcItfSignature();
          parameterTypes[i]= getClass().getClassLoader().loadClass(signature);
        }else{
          parameterTypes[i]= arguments[i].getClass();
        }
      }
      try {
        Method m = itf.getClass().getDeclaredMethod(methodName,parameterTypes);
        try{
          Object result = m.invoke(itf, (Object[])arguments);
          if (result instanceof Object[]){
            for(Object o : (Object[])result){
              System.out.println(o);
            }
          }else{
            System.out.println(result);
          }
          return result;
        }catch(InvocationTargetException e){
          throw e.getTargetException();
        } catch (IllegalArgumentException e) {
          e.printStackTrace();
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        }
      } catch (SecurityException e1) {
        System.out.println("SecurityException");
      } catch (NoSuchMethodException e1) {
        System.out.println("No such method");
      }
    } catch (NoSuchInterfaceException e1) {
      System.out.println("No such interface");
    }
    return null;
  }

  public String getName() {
    return "call";
  }
  
}
